package com.appcelerator.mq
{
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	public class MessageQueue {
		
		private var messages:Array;
		private var listeners:Array;
		private var filters:Array;
		private var timer:Timer;
		private static var GLOBAL:MessageQueue;

		/**
		 * Returns the singleton global message queue.  To create other message queues,
		 * feel free to use the constructor.
		 * 
		 * @param interval - the message queue scan interval - ignored if queue already instantiated
		 * @return - the global message queue
		 * 
		 */
		public static function getGlobal(interval:Number=150):MessageQueue {
			if (MessageQueue.GLOBAL == null) {
				MessageQueue.GLOBAL = new MessageQueue(interval);
			}
			return MessageQueue.GLOBAL;
		}
		
		/**
		 * Full Constructor
		 */
		public function MessageQueue(interval:Number=150) {
			this.messages = new Array();
			this.listeners = new Array();
			this.filters = new Array();
			this.timer = new Timer(interval);
			this.timer.addEventListener(TimerEvent.TIMER, this.scan);
			this.timer.start();
		}
		
		//shorthand for adding a message to the queue
		public function pub(name:String, payload:Object=null, scope:String="default"):void {
			var message:Message = new Message(name, payload, scope);
			this.messages.push(message);
		}
		
		//publish a message to the queue		
		public function publish(message:Message):void {
			this.messages.push(message);
		}
		
		//shorthand for adding a listener
		public function sub(pattern:Object, callback:Function, handle:String=null, scope:String="default"):void {
			var listener:Listener = new Listener(pattern, callback, handle, scope);
			this.listeners.push(listener);
		}
		
		//add a message listener
		public function subscribe(listener:Listener):void {
			this.listeners.push(listener);
		}
		
		//remove a listener from the array of listeners
		public function unsubscribe(handle:String):void {
			var temp:Array = new Array();
			for (var i:uint = 0; i < this.listeners.length; i++) {
				var listener:Listener = this.listeners[i];
				if (listener.handle != handle) {
					temp.push(listener);
				}
			}
			this.listeners = temp;
		}
		
		//Add a message filter
		public function filter(filter:Listener):void {
			this.filters.push(filter);
		}
		
		//remove an filter from the list
		public function unfilter(handle:String):void {
			var temp:Array = new Array();
			for (var i:uint = 0; i < this.filters.length; i++) {
				var filter:Listener = this.filters[i];
				if (filter.handle != handle) {
					temp.push(filter);
				}
			}
			this.filters = temp;
		}
		
		/**
		 * Scan this message queue for messages
		 */
		private function scan(timerEvent:TimerEvent):void {
			while (this.messages.length > 0) {
				var message:Message = this.messages.shift();
				
				//Let filters have at it
				var squash:Boolean = false;
				for (var i:uint = 0; i < this.filters.length; i++) {
					var filter:Listener = this.filters[i];
					if (filter.scope == message.scope) {
						if (filter.pattern is RegExp) {
							var regex:RegExp = filter.pattern as RegExp;
							if (regex.test(message.name)) {
								squash = filter.callback.call(this, message);
							}
						}
						else if (filter.pattern == message.name) {
							squash = filter.callback.call(this, message);
						}
					}
				}
				
				//once the filters have had their way, allow listeners to process
				//if the message was not squashed
				if (!squash) {
					for (i = 0; i < this.listeners.length; i++) {
						var listener:Listener = this.listeners[i];
						if (listener.scope == message.scope) {
							if (listener.pattern is RegExp) {
								regex = listener.pattern as RegExp;
								if (regex.test(message.name)) {
									listener.callback.call(this, message);
								}
							}
							else if (listener.pattern == message.name) {
								listener.callback.call(this, message);
							}
						}
					}
				}
			}
		}
		
	}
}